/**
 * Created by mintao on 16/4/28.
 * 敏感字检测
 */


/***
 * 敏感字文件
  * @type {{File1: string}}
 */
let SensitiveFilePath = {
    File1 : 'WordList/sensitiveword.txt',
};


////敏感词
// DFA 实现敏感词过滤 // improve from http://blog.sina.com.cn/s/blog_53f6148101016wq1.html
export default class SensitiveWordUtil{
    static m_wordList = [] // @private
    static m_map = null // @private

    static init () {
        this.loadFile(SensitiveFilePath.File1);
    }

    /***
     * 加载敏感字文件
     * @param filepath
     * @returns {Array}
     */
    static loadFile (filepath){
        let wordlist = [];
        cc.resources.load(filepath, (err, res:any)=>{
            let data = res.text;
            if (err){
                Log('--------load failed');
            }
            else{
                wordlist = data.split(/[,\n]/);
                this.m_map = this.BuildMap(wordlist);
            }
        });
    }

    /***
     * 替换 str 中的敏感词为 replaceChar,
     * step 为筛选强度， 最小为 0，最大为 5
     * @param str
     * @param replaceChar
     * @param step
     * @returns {*}
     * @constructor
     */
    static RunFilterWord (str, replaceChar, step) {
        if (str == undefined) {
            return "";
        }

        if (step > 5) {
            step = 5;
        }
        if (step < 0) {
            step = 0;
        }
        for (let j = 0; j < step; ++ j) {
            let result = this.Check(this.m_map, str);
            // 排序结果，长度长的在前面
            result.sort(function(a,b){
                return b.length - a.length
            });

            Log("result: " + result + ', result length : ' + result.length);
            for (let i = 0; i < result.length; i++) {
                let wordlen = result[i].length;
                let replaceStr = '';
                for (let k = 0; k < wordlen; ++ k){
                    replaceStr += replaceChar;
                }
                str = str.replace(result[i], replaceStr);
            }
        }

        return str;
    }

    // @private
    /***
     * 创建敏感词map
     * @param wordList
     * @returns {{}}
     * @constructor
     */
    static BuildMap (wordList) {
        let result = {};
        let count = wordList.length;
        Log('---------敏感词count: ', count);
        // 遍历单词
        for (let i = 0; i < count; ++ i) {
            let map = result;
            let word = wordList[i].replace('\r', '');
//            Log('-----敏感词word:', word);
            let wordLength = word.length;

            // 遍历单词的每个字母
            for (let j = 0; j < wordLength; ++ j) {
                let ch = word.charAt(j);
                let stateInfo = map[ch];
                if (stateInfo == null) {
                    stateInfo = {};
                    map[ch] = stateInfo;
                }

                // 如果是最后一个字母，设置一个完结标识
                if (j == wordLength - 1) {
                    stateInfo["isFinish"] = true;
                }
                else {
                    map = stateInfo;
                }
            }
        }

        return result;
    }

    // @private //
    /***
     * 找出 content 中的敏感词
     * 返回一个包含敏感词的列表
     * @param map
     * @param content
     * @returns {Array}
     * @constructor
     */
    static Check (map, content) {
        let result = [];
        let count = content.length;
        let stack = [];
        let point = map;
        let isFound = false;//用于标记找到关键词的标记
        let foundStack = null;

        for (let i = 0; i < count; ++ i) {
            let ch = content.charAt(i);
            let item = point[ch];
            // 如果没找到则复位，让主循环
            if (item == null) {
                if (isFound) {
                    isFound = false;
                    i = i - (stack.length - foundStack.length + 1); // 计算回退距离
                    result.push(foundStack.join("")); // 把单个的字母数组连成一串字符串
                }
                else {
                    i = i - stack.length; // 计算回退距离
                }
                stack = []; // 清空字符堆栈
                point = map;
            }
            else if (item["isFinish"]) {
                stack.push(ch);
                point = item;
                isFound = true;//标记找到了目标词
                foundStack = stack.concat(); // 复制数组
            }
            else {
                stack.push(ch);
                point = item;
            }
        }
        // 这里还要补充检查
        if (isFound) {
            result.push(foundStack.join("")); // 把单个的字母数组连成一串字符串
        }
        return result;
    }

    /***
     * 销毁敏感字内存 暂未用到
     * @constructor
     */
    static Destroy () {
        this.m_wordList = [];
        this.m_map = null;
    }

    /***
     * test 暂未用到
     * @param wordList
     * @returns {{}}
     */
    static TestbuildMap (wordList) {
        let result = {};
        let count = wordList.length;
        for (let i = 0; i < count; ++i) {
            let map = result;
            let word = wordList[i];
            for (let j = 0; j < word.length; ++j) {
                let ch = word.charAt(j);
                if (typeof(map[ch]) != "undefined") {
                    map = map[ch];
                    if (map["empty"]) {
                        break;
                    }
                }
                else {
                    if (map["empty"]) {
                        delete map["empty"];
                    }
                    map[ch] = {"empty":true};
                    map = map[ch];
                }
            }
        }
        return result;
    }

    /***
     * 测试检测 暂未用到
     * @param map
     * @param content
     * @returns {Array}
     */
    static testCheck (map, content) {
        let result = [];
        let count = content.length;
        let stack = [];
        let point = map;
        for (let i = 0; i < count; ++ i) {
            let ch = content.charAt(i);
            let item = point[ch];
            if (typeof(item) == "undefined") {
                i = i - stack.length;
                stack = [];
                point = map;
            }
            else if (item["empty"]) {
                stack.push(ch);
                result.push(stack.join(""));
                stack = [];
                point = map;
            }
            else {
                stack.push(ch);
                point = item;
            }
        }
        return result;
    }
};
